# coding=utf-8
# author=haishan09@gmail.com
import copy
import logging
import requests
from cube.providers.base import BaseProvider
from common.provider import provider_register

logger = logging.getLogger('KylinProvider')

class KylinQueryError(Exception):
    pass


@provider_register('cube', 'kylin')
class kylinProvider(BaseProvider):

    temp_select_sql = '''
        select {columns} from {table} where {where} group by {group_by}
    '''

    def _initial(self):
        self.uri= self.object.dconf['uri']
        self.basic_user = self.object.dconf['basic_user']
        self.basic_password = self.object.dconf['basic_password']

    def convert_aggregate_value(self, value):
        if '.' in value:
            return float(value)
        return int(value)

    def construct_sql(self, table, columns, group_by, where=[], having=[], order_by=[]):
        where = where and ' and '.join(where) or '1=1'
        sql = self.temp_select_sql.format(**{
            'table': table,
            'columns': ','.join(columns),
            'group_by': ','.join(group_by),
            'where': where
        })
        if having:
            sql += ' having {0}'.format(' and '.join(having))
        if order_by:
            sql += ' order by ' + ','.join(map(lambda o: ' '.join(o), order_by))
        return sql

    def get_aggregate_expression(self, agg_name):
        from aggregate.models import Aggregate
        expression = ''
        try:
            aggobj = Aggregate.objects.get(name=agg_name)
        except Exception as e:
            logger.error('获取表达式时发生错误！指标：{0}'.format(agg_name))
            raise e
        function = aggobj.function
        measure_name = aggobj.measure.name
        if function == 'count_distinct':
            expression = 'count(distinct ' + measure_name + ')'
        else:
            expression = function + '(' + measure_name + ')'
        return expression


    def request(self, config):
        cube = config['cube']
        project = cube.dconf['project']
        table_sql = cube.dconf['table_sql']
        table = table_sql.startswith('select ') and '({0})'.format(table_sql) or table_sql
        offset = config['offset']
        limit = config['limit']
        dimensions = map(lambda d: d['drilldown'], config['drilldowns'])
        aggregates = []
        columns = copy.copy(dimensions)
        dim_cnt = len(dimensions)
        for aggconf in config['aggregates']:
            name = aggconf['aggregate']
            columns.append(self.get_aggregate_expression(name))
            aggregates.append(name)
        group_by = dimensions
        order_by = []
        for orderbyconf in config['orderbys']:
            by = orderbyconf['order_by']
            order = orderbyconf['order']
            if cube.is_dim_member(by):
                order_by.append((by, order))
            else:
                order_by.append((self.get_aggregate_expression(by), order))
        where = []
        having = []
        for cutconf in config['cuts']:
            cut = cutconf['cut']
            condition = cutconf['condition']
            value = cutconf['value']
            dim = cube.is_dim_member(cut)
            if dim:
                value = dim.provider_instance.handle_choice(value)
                if condition == 'eq':
                    where.append(cut + ' = {0}'.format(value))
                elif condition == 'not in':
                    where.append(cut + ' not in ({0})'.format(value))
            else:
                exp = self.get_aggregate_expression(cut)
                if condition == 'lt':
                    having.append(exp + ' < {0}'.format(value))
                elif condition == 'gt':
                    having.append(exp + ' > {0}'.format(value))
        sql = self.construct_sql(table, columns, group_by, where=where, having=having, order_by=order_by)
        logger.info('kylin sql: {0}'.format(sql))
        data = {
            'sql': sql,
            'offset': offset,
            'limit': limit,
            'project': project
        }
        res = requests.post(self.uri, json=data, auth=(self.basic_user, self.basic_password))
        resjson = res.json()
        if res.status_code != 200:
            raise KylinQueryError(resjson)
        retdict = {
            'total_records': 0,
            'summary': {},
            'cells': []
        }
        # column_metas = resjson['columnMetas']
        retdict['total_records'] = resjson['totalScanCount']
        for record in resjson['results']:
            dim_part = zip(dimensions, record[:dim_cnt])
            agg_part = zip(aggregates, map(lambda v: self.convert_aggregate_value(v), record[dim_cnt:]))
            retdict['cells'].append(dict(dim_part + agg_part))
        return retdict

    def handle_cuts(self):
        pass


